#ifndef AVALON_OUT_HPP
#define AVALON_OUT_HPP
#include <boost/noncopyable.hpp>
#include "Avalon/Base/Base.hpp"

namespace Avalon {
namespace Details {
  template<typename T>
  struct OutMover {
    typedef Out<T> Type;

    struct Holder {
      T& m_result;

      Holder(T& result)
          : m_result(result) {}
    };

    Holder operator ()(T& result) const {
      return Holder(result);
    }
  };

  template<typename T>
  struct OutMover<Out<T> > : OutMover<T> {
    typedef typename OutMover<T>::Type Type;
    typedef typename OutMover<T>::Holder Holder;

    Holder operator ()(Out<T>& result) const {
      return OutMover<T>()(*result.Get());
    }
  };
}

  /*! \class Out
   *  \brief Used to identify a parameter as an output.
   */
  template<typename T>
  class Out : private boost::noncopyable {
    public:

      ~Out() {}

      //! Returns a reference to the result.
      T& operator *() {
        return *m_result;
      }

      //! Returns a reference to the result.
      const T& operator *() const {
        return *m_result;
      }

      //! Returns a pointer to the result.
      T* operator ->() {
        return m_result;
      }

      //! Returns a pointer to the result.
      const T* operator ->() const {
        return m_result;
      }

      //! Returns a pointer to the result.
      T* Get() {
        return m_result;
      }

      //! Returns a pointer to the result.
      const T* Get() const {
        return m_result;
      }

    private:
      template<typename T>
      friend typename Details::OutMover<T>::Type Store(T& result);
      T* m_result;

      Out(const typename Details::OutMover<T>::Holder& holder)
          : m_result(&holder.m_result) {}
  };

  //! Indicates that a parameter is being used as the result of a function.
  /*!
    \param result Where the result of the function is stored.
    \return An Out to the <i>result</i>.
  */
  template<typename T>
  typename Details::OutMover<T>::Type Store(T& result) {
    return Details::OutMover<T>()(result);
  }
}

#endif // AVALON_OUT_HPP
